package com.dayuanit.dy15.ebook.bookshop.service.impl;

import com.dayuanit.dy15.ebook.bookshop.domain.OrderDetailDomain;
import com.dayuanit.dy15.ebook.bookshop.domain.OrderDomain;
import com.dayuanit.dy15.ebook.bookshop.dto.CardDTO;
import com.dayuanit.dy15.ebook.bookshop.dto.OrderDTO;
import com.dayuanit.dy15.ebook.bookshop.entity.Address;
import com.dayuanit.dy15.ebook.bookshop.entity.Commodity;
import com.dayuanit.dy15.ebook.bookshop.entity.Order;
import com.dayuanit.dy15.ebook.bookshop.entity.OrderDetail;
import com.dayuanit.dy15.ebook.bookshop.enums.OrderStatusEnum;
import com.dayuanit.dy15.ebook.bookshop.enums.PayChannelEnum;
import com.dayuanit.dy15.ebook.bookshop.exception.BizException;
import com.dayuanit.dy15.ebook.bookshop.mapper.AddressMapper;
import com.dayuanit.dy15.ebook.bookshop.mapper.CommodityMapper;
import com.dayuanit.dy15.ebook.bookshop.mapper.OrderDetailMapper;
import com.dayuanit.dy15.ebook.bookshop.mapper.OrderMapper;
import com.dayuanit.dy15.ebook.bookshop.rabbitmq.RabbitMqHandler;
import com.dayuanit.dy15.ebook.bookshop.service.OrderService;
import com.dayuanit.dy15.ebook.bookshop.util.DateUtil;
import com.dayuanit.dy15.ebook.bookshop.util.MoneyUtil;
import com.dayuanit.dy15.ebook.bookshop.vo.SettlementRequestVO;
import com.dayuanit.dy15.pay.dy15payspringbootstarter.config.PayNotifyResult;
import com.dayuanit.dy15.pay.dy15payspringbootstarter.handler.AlipayHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
//    private static final Logger logger = LoggerFactory.getLogger(OrderService.class);

    @Value("${ebook.picture.url}")
    private String pictureUrl;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapperl;

    @Autowired
    private CommodityMapper commodityMapper;

    @Autowired
    private AddressMapper addressMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private AlipayHandler alipayHandler;

    @Autowired
    private RabbitMqHandler rabbitMqHandler;

    @Override
    public void createOrder(List<SettlementRequestVO> commoditys) {
        Order order = new Order();
        for (SettlementRequestVO vo : commoditys) {
        }
    }

    @Override
    public List<CardDTO> listSettlement(List<SettlementRequestVO> settlementVO) {

        //方案一：
//        List<Object> ids = new ArrayList<>(settlementVO.size());
//        for (SettlementRequestVO vo : settlementVO) {
//            ids.add(vo.getCommodityId());
//        }

        //方案二：lambda写法
//        List<Long> collect = settlementVO.stream().map(vo -> vo.getCommodityId()).collect(Collectors.toList());

        //方案三：函数方法  墙裂推荐
        List<Long> ids = settlementVO.stream().map(SettlementRequestVO::getCommodityId).collect(Collectors.toList());

        //方案一： 把List<SettlementRequestVO> 转为Map集合 k-商品ID v-SettlementRequestVO对象
//        Map<Long, SettlementRequestVO> voMap = new HashMap<>();
//        for (SettlementRequestVO vo : settlementVO) {
//            voMap.put(vo.getCommodityId(), vo);
//        }

        //方案二： 把List<SettlementRequestVO> 转为Map集合 k-商品ID v-SettlementRequestVO对象
        Map<Long, SettlementRequestVO> voMap = settlementVO.stream().collect(Collectors.toMap(SettlementRequestVO::getCommodityId, vo -> vo));

        List<Commodity> commodities = commodityMapper.listByIds(ids);
        List<CardDTO> dtos = new ArrayList<>(commodities.size());

        // 代码中不允许有两层以上的嵌套循环 如果有 则优化之。
        for (Commodity commodity : commodities) {
            CardDTO cardDTO = new CardDTO();
            dtos.add(cardDTO);

            cardDTO.setCommodityName(commodity.getCommoName());
            cardDTO.setPrice(commodity.getPrice());
            cardDTO.setPictureUrl(pictureUrl + commodity.getPictureName());
            cardDTO.setCommodityId(commodity.getId());

            //方案一：for查找 不推荐多层循环嵌套 影响效率
//            for (SettlementRequestVO v : settlementVO) {
//                if (v.getCommodityId() == commodity.getId()) {
//                    cardDTO.setBuyNum(v.getBuyNum());
//                    break;
//                }
//            }

            //  方案二：通过散列表查找 优化两层for循环
            cardDTO.setBuyNum(voMap.get(commodity.getId()).getBuyNum());
        }

        return dtos;
    }

    /**
     * 下单  创建订单
     * @param userId
     * @param addressId
     * @param payChannel
     * @param commoditys
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createOrder(long userId, long addressId, int payChannel, List<SettlementRequestVO> commoditys) {

        Address address = addressMapper.selectByPrimaryKey(addressId);

        List<Long> commodityIds = commoditys.stream().map(SettlementRequestVO::getCommodityId).collect(Collectors.toList());

        // k-商品ID  v-SettlementRequestVO
        Map<Long, SettlementRequestVO> voMap = commoditys.stream().collect(Collectors.toMap(vo -> vo.getCommodityId(), vo -> vo));

        //购买的所有商品
        List<Commodity> commodities = commodityMapper.listByIds(commodityIds);
        String totalAmount = "0";
        for (Commodity commodity :  commodities) {
            totalAmount = MoneyUtil.add(totalAmount, MoneyUtil.mul(commodity.getPrice(), String.valueOf(voMap.get(commodity.getId()).getBuyNum()))) ;
        }

        // 创建订单 TODO 支付方式木有记录 无法退款或者对账
        Order order = new Order();
        order.setAddress(address.getProvince() + address.getCity() + address.getCity() + address.getDetailAddr());
        order.setCreateTime(new Date());
        order.setModifyTime(order.getCreateTime());
        order.setPhone(address.getPhone());
        order.setRealName(address.getRealName());
        order.setStatus(OrderStatusEnum.待支付.getK());
        order.setTotalAmount(totalAmount);
        order.setUserId(userId);
        orderMapper.insert(order);

        // 订单详情
        for (Commodity commodity : commodities) {

            // 扣减商品库存
            int newStock = commodity.getStock() - voMap.get(commodity.getId()).getBuyNum();
            if (newStock < 0) {
                throw new BizException(commodity.getCommoName() + "库存不足");
            }

            int rows = commodityMapper.updateStock(commodity.getId(), commodity.getStock(), newStock);
            if (1 != rows) {
                throw new BizException("下单失败");
            }

            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setBuyNum(voMap.get(commodity.getId()).getBuyNum());
            orderDetail.setCommodityId(commodity.getId());
            orderDetail.setCommodityName(commodity.getCommoName());
            orderDetail.setOrderId(order.getId());
            orderDetail.setPrice(commodity.getPrice());
            orderDetailMapper.insert(orderDetail);
        }

        String payForm = null;
        // 根据支付方式 生成对应的收银台请求参数
        if (payChannel == PayChannelEnum.支付宝.getK()) {
            payForm = alipayHandler.generatePayForm(order.getId(), order.getTotalAmount());
        } else if (payChannel == PayChannelEnum.ATM.getK()) {
            throw new BizException("暂不支持ATM支付");
        }

        //将新订单发送到延迟队列中
        rabbitMqHandler.sendOrderToDealyQueue(order.getId());
        return payForm;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processAlipayNotify(Map<String, String> notifyParam) {

        //校验异步通知参数
        PayNotifyResult payNotifyResult = alipayHandler.processNotify(notifyParam);
        //如果校验未通过，则抛异常
        if (!payNotifyResult.isPaySuccess()) {
            throw new BizException("异步通知处理失败");
        }

        Order order = orderMapper.selectByPrimaryKey(Long.valueOf(payNotifyResult.getOrderId()));
        if (null == order) {
            throw new BizException("订单不存在");
        }

        //比较支付金额和订单金额是否相等
//        if (Double.parseDouble(order.getTotalAmount()) != Double.parseDouble(payNotifyResult.getPayAmount())) {
//            throw new BizException("订单金额不一致");
//        }

        if (order.getStatus() == OrderStatusEnum.待发货.getK()) {
            return;
        }

        //订单的状态校验
        if (order.getStatus() != OrderStatusEnum.待支付.getK()) {
            return;
        }

        // 修改订单状态为待发货
        int rows = orderMapper.updateStatus(order.getId(), order.getStatus(), OrderStatusEnum.待发货.getK());
        if (1 != rows) {
            throw new BizException("异步通知修改订单状态失败");
        }
    }


    /**
     * 关闭订单
     * @param orderId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeOrder(long orderId) {
        // 订单是否已经支付成功

        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order.getStatus() == OrderStatusEnum.已取消.getK()) {
            return;
        }

        if (order.getStatus() != OrderStatusEnum.待支付.getK()) {
            return;
        }

        // 若没有成功 则将其状态改为已关闭
        int rows = orderMapper.updateStatus(orderId, order.getStatus(), OrderStatusEnum.已取消.getK());
        if (1 != rows) {
            throw new BizException("订单取消失败");
        }

        // 再将库存还原
        List<OrderDetail> orderDetails = orderDetailMapper.listByOrderId(orderId);
        for (OrderDetail orderDetail : orderDetails) {
            Commodity commodity = commodityMapper.selectByPrimaryKey(orderDetail.getCommodityId());
            int newStock = commodity.getStock() + orderDetail.getBuyNum();
            rows = commodityMapper.plusStock(commodity.getId(), orderDetail.getBuyNum());
            if (1 != rows) {
                throw new BizException("订单取消失败");
            }
        }

    }


    @Override
    public List<OrderDTO> listOrder(long userId) {
        List<Order> orders = orderMapper.listOrder(userId);
        List<OrderDTO> listOrderDTO = new ArrayList<>(orders.size());

        for (Order order : orders) {
            OrderDTO orderDTO = new OrderDTO();
            listOrderDTO.add(orderDTO);

            orderDTO.setCreateTime(DateUtil.date2String(order.getCreateTime()));
            orderDTO.setOrderId(order.getId());
            orderDTO.setOrderStatus(order.getStatus());
            orderDTO.setRealName(order.getRealName());
            orderDTO.setStatusDesc(OrderStatusEnum.get(order.getStatus()).getV());
            orderDTO.setTotalAmount(order.getTotalAmount());

            //订单明细集合
            List<OrderDTO.OrderDetailDTO> orderDetailDTOList = new ArrayList<>();
            //将订单明细集合赋值给orderDTO
            orderDTO.setOrderDetails(orderDetailDTOList);

            //对订单明细集合添加明细信息
            List<OrderDetailDomain> orderDetailDomains = orderDetailMapper.list4OrderInfo(order.getId());
            for (OrderDetailDomain orderDetailDomain : orderDetailDomains) {
                OrderDTO.OrderDetailDTO orderDetailDTO = new OrderDTO.OrderDetailDTO();
                orderDetailDTOList.add(orderDetailDTO);

                orderDetailDTO.setBuyNum(orderDetailDomain.getBuyNum());
                orderDetailDTO.setCommodityName(orderDetailDomain.getCommodityName());
                orderDetailDTO.setPictureUrl(pictureUrl + orderDetailDomain.getPictureName());
            }
        }

        return listOrderDTO;
    }

    /**
     * 推荐此种方法查询我的订单
     * 只需要查询两次数据库
     *
     * 而方法一和三 都是要查询N+1次
     * @param userId
     * @return
     */
    @Override
    public List<OrderDTO> listOrder2(long userId) {

        //TODO 先查询所有的订单  order
        List<Order> orders = orderMapper.listOrder(userId);

        //TODO 然后再通过订单ID到订单明细表查询所有的明细 order_detail

        List<Long> orderIds = orders.stream()
                .map(order -> order.getId())
                .collect(Collectors.toList());

        //所有订单的明细  不是某一个订单的明细
        List<OrderDetailDomain> orderDetailDomains = orderDetailMapper.list4OrderInfo2(orderIds);
        List<OrderDTO.OrderDetailDTO> orderDetailsDTO = orderDetailDomains.stream()
                .map(detail -> {
                    OrderDTO.OrderDetailDTO orderDetailDTO = new OrderDTO.OrderDetailDTO();
                    orderDetailDTO.setBuyNum(detail.getBuyNum());
                    orderDetailDTO.setCommodityName(detail.getCommodityName());
                    orderDetailDTO.setPictureUrl(pictureUrl + detail.getPictureName());
                    orderDetailDTO.setOrderId(detail.getOrderId());
                    return orderDetailDTO;
                })
                .collect(Collectors.toList());

        //k-订单ID  v-订单明细
        Map<Long, List<OrderDTO.OrderDetailDTO>> detailMap = orderDetailsDTO.stream()
                .collect(Collectors.groupingBy(detail -> detail.getOrderId()));

        //循环总订单
        List<OrderDTO> dtos = new ArrayList<>(orders.size());
        for (Order order : orders) {
            OrderDTO orderDTO = new OrderDTO();
            dtos.add(orderDTO);

            orderDTO.setCreateTime(DateUtil.date2String(order.getCreateTime()));
            orderDTO.setOrderId(order.getId());
            orderDTO.setOrderStatus(order.getStatus());
            orderDTO.setRealName(order.getRealName());
            orderDTO.setStatusDesc(OrderStatusEnum.get(order.getStatus()).getV());
            orderDTO.setTotalAmount(order.getTotalAmount());

            orderDTO.setOrderDetails(detailMap.get(order.getId()));
        }

        return dtos;
    }


    @Override
    public List<OrderDTO> listOrder3(long userId) {

        List<OrderDomain> orderDomains = orderMapper.listOrderInfos(userId);

        //k-订单ID  v-OrderDetailDTO
        Map<Long, List<OrderDTO.OrderDetailDTO>> orderDetailMap = orderDomains.stream()
                .flatMap(orderDomain -> orderDomain.getDetailDomains().stream())
                .map(domain -> {
                    OrderDTO.OrderDetailDTO orderDetailDTO = new OrderDTO.OrderDetailDTO();
                    orderDetailDTO.setBuyNum(domain.getBuyNum());
                    orderDetailDTO.setCommodityName(domain.getCommodityName());
                    orderDetailDTO.setPictureUrl(pictureUrl + domain.getPictureName());
                    orderDetailDTO.setOrderId(domain.getOrderId());
                    return orderDetailDTO;
                })
                .collect(Collectors.groupingBy(detailDomain -> detailDomain.getOrderId()));


        List<OrderDTO> dtos = new ArrayList<>(orderDomains.size());

        for (OrderDomain orderDomain : orderDomains) {
            OrderDTO orderDTO = new OrderDTO();
            orderDTO.setCreateTime(DateUtil.date2String(orderDomain.getCreateTime()));
            orderDTO.setOrderId(orderDomain.getOrderId());
            orderDTO.setOrderStatus(orderDomain.getOrderStatus());
            orderDTO.setRealName(orderDomain.getRealName());
            orderDTO.setStatusDesc(OrderStatusEnum.get(orderDomain.getOrderStatus()).getV());
            orderDTO.setTotalAmount(orderDomain.getTotalAmount());
            orderDTO.setOrderDetails(orderDetailMap.get(orderDomain.getOrderId()));
            dtos.add(orderDTO);
        }

        return dtos;
    }
}
